﻿#region File Description
//-----------------------------------------------------------------------------
// Task
//
// Copyright © 2013 Wave Coorporation. All rights reserved.
// Use is subject to license terms.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using WaveEngine.Extensions.ITasks;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using WaveEngine.Framework;
using WaveEngine.Framework.Animation;
using WaveEngine.Framework.Services;
#endregion

namespace WaveEngine.Extensions.ITasks
{
    public abstract class TaskBase : ITask
    {
        /// <summary>
        /// Gets the name of the task.
        /// </summary>
        /// <value>
        /// The name of the task.
        /// </value>
        public string TaskName { get; private set; }

        /// <summary>
        /// The skippable
        /// </summary>
        internal bool IsSkippable;

        /// <summary>
        /// Occurs when [completed].
        /// </summary>
        public event Action Completed;

        /// <summary>
        /// Occurs when [cancelled].
        /// </summary>
        public event Action Cancelled;

        /// <summary>
        /// Occurs when [skipped].
        /// </summary>
        public event Action Skipped;

        /// <summary>
        /// Gets the parent.
        /// </summary>
        /// <value>
        /// The parent.
        /// </value>
        internal ITask Parent { get; private set; }

        /// <summary>
        /// Gets the state of the task.
        /// </summary>
        /// <value>
        /// The state of the task.
        /// </value>
        public TaskState TaskState { get; internal set; }

        /// <summary>
        /// Gets the scene.
        /// </summary>
        /// <value>
        /// The scene.
        /// </value>
        public Scene Scene { get; private set; }

        /// <summary>
        /// Gets the child tasks.
        /// </summary>
        /// <value>
        /// The child tasks.
        /// </value>
        virtual public IEnumerable<ITask> ChildTasks
        {
            get
            {
                return System.Linq.Enumerable.Empty<ITask>();
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskBase" /> class.
        /// </summary>
        /// <param name="scene">The scene.</param>
        /// <param name="taskName">Name of the task.</param>
        protected TaskBase(Scene scene, string taskName)
        {
            this.IsSkippable = false;
            this.TaskState = TaskState.None;
            this.Scene = scene;
            this.TaskName = taskName;

            var TaskContext = scene as ITaskContext;
            if (TaskContext != null )
            {
                this.TaskName = taskName ?? this.GetType().Name + "-" + Guid.NewGuid().ToString();
                TaskContext.RegisterTask(this.TaskName, this);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TaskBase" /> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="taskName">Name of the task.</param>
        /// <exception cref="System.ArgumentException">parent task cannot be null. if is a root task, use the other constructor</exception>
        protected TaskBase(ITask parent, string taskName)
        {
            this.IsSkippable = false;
            if (parent == null)
                throw new ArgumentException("parent task cannot be null. if is a root task, use the other constructor");

            this.Parent = parent;
            parent.Completed += this.Run;

            //parent.Cancelled += this.Cancel;

            this.TaskState = TaskState.Waiting;
            this.Scene = parent.Scene;
            this.TaskName = taskName;

            var TaskContext = parent.Scene as ITaskContext;
            if (TaskContext != null)
            {
                this.TaskName = taskName ?? this.GetType().Name.ToString() + "-" + Guid.NewGuid().ToString();
                TaskContext.RegisterTask(this.TaskName, this);
            }
        }

        /// <summary>
        /// Ons the run.
        /// </summary>
        [DebuggerStepThrough]
        protected abstract void performRun();


        /// <summary>
        /// Runs this instance.
        /// </summary>
        /// <exception cref="System.NotSupportedException"></exception>
        ////[DebuggerStepThrough]
        public virtual void Run()
        {
            // Ignore the run if the scene is already disposed
            if (Scene != null && Scene.IsDisposed)
                return;

            if (this.TaskState == TaskState.Finished
                || this.TaskState == TaskState.Aborted
                || this.TaskState == TaskState.Running)
            {
                throw new NotSupportedException(string.Format("The task cannot be runned because its state ({0}) does not allow it", this.TaskState.ToString()));
            }

            if (this.Parent == null
                || this.Parent.TaskState == TaskState.Finished)
            {
                this.TaskState = TaskState.Running;
                this.performRun();
            }
            /*
        else if (this.SkipFlag &&  this.TaskState==TaskState.Waiting && this.Completed != null)
        {
            this.TaskState = TaskState.Finished;
            this.Completed();
            return;
        }
        else if (this.SkipFlag && this.TaskState != TaskState.Running)
        {
            //DO NONE, SKIP COMPLETED
        }
             * */
            else if (this.Parent.TaskState == TaskState.None || this.Parent.TaskState == TaskState.Waiting)
            {
                //RUN UP
                this.Parent.Run();
            }

            else
            {
#if DEBUG
                throw new NotSupportedException("Incoherent Task State");
#endif
            }
        }

        /// <summary>
        /// Skips this instance.
        /// </summary>
        /// <exception cref="System.NotImplementedException"></exception>
       // [DebuggerStepThrough]
        public bool TrySkip()
        {
            if (this.TaskState == TaskState.Running)
            {
                // Skip this Task
                return this.performSkip();

            }
            else if (this.Parent != null && (this.Parent.TaskState == TaskState.Waiting
                || this.Parent.TaskState == TaskState.Running))
            {
                // Skip the parent Task
                return this.Parent.TrySkip();
            }
            else if (this.TaskState == TaskState.None ||
              this.TaskState == TaskState.Aborted ||
              this.TaskState == TaskState.Finished)
            {
#if DEBUG
                throw new NotSupportedException("It cannot be canncelled because it has not been started or already has been cancelled");
#endif
            }
            return true;
        }

        /// <summary>
        /// Cancels this instance.
        /// </summary>
        /// <exception cref="System.NotSupportedException">It cannot be canncelled because it has not been started</exception>
        [DebuggerStepThrough]
        public void Cancel()
        {
            if (this.TaskState == TaskState.Running)
            {
                //CANCEL THIS
                this.performCancel();
            }
            else if (this.Parent != null && this.TaskState == TaskState.Waiting)
            {
                //CANCEL UP
                this.Parent.Cancel();
            }
            else if (
                this.TaskState == TaskState.None ||
                this.TaskState == TaskState.Aborted ||
                this.TaskState == TaskState.Finished)
            {
#if DEBUG
                throw new NotSupportedException("It cannot be canncelled because it has not been started or already has been cancelled");
#endif
            }
        }

        /// <summary>
        /// Notifies the completed.
        /// </summary>
        [DebuggerStepThrough]
        protected void performCompleted()
        {
            if (this.TaskState == TaskState.Running)
            {
                this.TaskState = TaskState.Finished;
                if (this.Completed != null)
                    Completed();
            }
            else
            {
#if DEBUG
                throw new NotSupportedException("The task cannot be completed. Incoherent current task state");
#endif
            }
        }

        /// <summary>
        /// Notifies the cancelled.
        /// </summary>
        [DebuggerStepThrough]
        protected virtual void performCancel()
        {
            this.TaskState = TaskState.Aborted;
            if (this.Cancelled != null)
                this.Cancelled();
        }

        /// <summary>
        /// Notifies the skip.
        /// </summary>
        [DebuggerStepThrough]
        protected virtual bool performSkip()
        {
            if (this.IsSkippable)
            {
                if (this.TaskState == TaskState.Running)
                {
                    //this.SkipFlag = true;
                    this.TaskState = TaskState.Finished;
                    if (this.Skipped != null)
                        this.Skipped();

                    if (this.Completed != null)
                        this.Completed();

                    return true;
                }
                else
                {
#if DEBUG
                    throw new NotSupportedException("Incoherent current task state");
#else
                    return false;
#endif
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.GetType().Name + ": " + this.TaskName;
        }
    }
}
